#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>
#include "symbol.h"
#include "node.h"
#include "global.h"
#include "semantic.h"
#include "hashtable.h"

static void semantic_parse_dep(node * node1, list_t argnames);

static void semantic_parse_expr(node * node1, list_t argnames, int isconst);

static void sem_analysis_fn(symbol * fnsym, node * dep);

static void sem_simple_fn(symbol * fnsym);

static void sem_block_child(symbol * fnsym, symbol * childfnsym);

static void sem_fatal(const char * format, ...)
{
  va_list args;

  va_start(args, format);

  fprintf(stderr, "error: ");
  vfprintf(stderr, format, args);
  fprintf(stderr, "\n");

  va_end(args);

  exit(1);  
}

/* a second parse of the model */
void semantic_parse_second(list_t obslist, list_t querylist)
{
  symbol * sym;
  list_t iter;
  list_t iter2;
  
  int k;                                     /* the k of RkVector */

  /* scan all the symbols and store the relationships between them */
  for(iter = symbol_table_itr_start(); 
      (sym = (symbol *)hashtable_itr_next(iter));)
  {
    switch(sym->symtype)
    {
    case SYMBOL_B_TYPE:

      if (!strcmp(sym->name, "Real"))
      {
        sym->genname = "double";
        sym->t.gendeclname = "double";
        sym->t.genstruct = 0;
        sym->t.repr_double = 1;
      }
      else if (!strcmp(sym->name, "NaturalNum"))
      {
        sym->genname = "int";
        sym->t.gendeclname = "int";
        sym->t.genstruct = 0;
        sym->t.repr_int = 1;
      }
      else if (!strcmp(sym->name, "C_Boolean"))
      {
        sym->genname = "int";
        sym->t.gendeclname = "int";
        sym->t.genstruct = 0;
        sym->t.repr_int = 1;
      }
      else if (!strcmp(sym->name, "Boolean"))
      {
        sym->genname = "Boolean_t";
        sym->t.gendeclname = "Boolean_t *";
        sym->t.genstruct = 1;
        sym->t.repr_pointer = 1;
      }
      else if (!strcmp(sym->name, "String"))
      {
        sym->genname = "String";
        sym->t.gendeclname = "char *";
        sym->t.genstruct = 0;
        sym->t.repr_pointer = 1;
      }
      else if (1 == sscanf(sym->name, "R%dVector", &k))
      {
        sym->genname = "RkVector";
        sym->t.gendeclname = "RkVector *";
        sym->t.genstruct = 0;
        sym->t.repr_pointer = 1;
      }
      else
      {
        if (sym->t.has_obsobjs && !sym->t.null_numfn 
            && list_is_empty(sym->t.origfns))
          sem_fatal("type '%s' has observed objects but no number statement",
                    sym->name);

        if (!symbol_has_fixed_objects(sym) && !sym->t.null_numfn 
            && list_is_empty(sym->t.origfns) && !sym->t.infinite)
          sem_fatal("type '%s' has neither guaranteed objects nor number "
                    "statements", sym->name);
        
        sym->genname = (char *)malloc(sizeof(char) * (strlen(sym->name) + 10));
        sprintf(sym->genname, "%s_b_t", sym->name);
       
        /*
        if ((!list_is_empty(sym->t.objlist)) && (list_is_empty(sym->t.attrs)))
        */
        sym->t.genstruct = 1;
        sym->t.gendeclname = malloc(strlen(sym->genname) + 10);
        sprintf(sym->t.gendeclname, "%s *", sym->genname);
        sym->t.repr_pointer = 1;
      }
      
      break;
      
    case SYMBOL_B_FUNCTION:
      switch(sym->f.category)
      {
      case SYMBOL_FN_CAT_RANDOM:
        /* the function better be defined */
        if (!sym->f.argname_list)
          sem_fatal("missing definition for random function '%s'",
                    sym->name);

        for (iter2 = list_next(sym->f.childattrs); iter2;
             iter2 = list_next(iter2))
        {
          sem_block_child(sym, (symbol *) list_item(iter2));
        }
        
        break;

      case SYMBOL_FN_CAT_ORIGIN:
        if (!sym->f.numfn)
          sem_fatal("missing number statement for origin function '%s'",
                    sym->name);

        for (iter2 = list_next(sym->f.childattrs); iter2;
             iter2 = list_next(iter2))
        {
          sem_block_child(sym, (symbol *) list_item(iter2));
        }

        break;

      default:
        break;
      }

      sym->genname = (char *)malloc(sizeof(char) * (strlen(sym->name) + 5));
      sprintf(sym->genname, "%s_b_f", sym->name);

      break;

    case SYMBOL_B_OBJECT:
      sym->genname = (char *) malloc(sizeof(char) * (strlen(sym->name)+5));
      sprintf(sym->genname, "%s_b_o", sym->name);
      
      break;

    default:
      fprintf(stderr, "unknown type %s", sym->name);
      exit(1);
    }
  }
  
  hashtable_itr_end(iter);
}

static void semantic_parse_dep(node * node1, list_t argnames)
{
  node * node2;
  node * node3;
  node * node4;

  switch(node1->type)
  {
  case NODETYPE_NULL:
  case NODETYPE_INTEGER:
  case NODETYPE_REAL:
  case NODETYPE_STRING:
  case NODETYPE_IDENTIFIER:
  case NODETYPE_RkVECTOR:
  case NODETYPE_FN_CALL:
  case NODETYPE_EQUAL:
  case NODETYPE_NOT_EQUAL:
  case NODETYPE_CPD_CALL:
  case NODETYPE_SYMBOL:
    node_error(node1, "illegal expression");
    break;


  case NODETYPE_IF:
    if (2 == list_count(node1->childlist))
    {
      list_item2(node1->childlist, (void **)&node2, (void **)&node3);
      node4 = NULL;
    }
    else
      list_item3(node1->childlist, (void **)&node2, (void **)&node3,
                 (void **)&node4);

    semantic_parse_expr(node2, argnames, 0);
    if (strcmp(node2->valtype->name, "Boolean") &&
        strcmp(node2->valtype->name, "C_Boolean"))
      node_error(node2, "expecting boolean condition");

    semantic_parse_dep(node3, argnames);
    if (node4)
      semantic_parse_dep(node4, argnames);
    break;
    
  case NODETYPE_DET_DEP:
  case NODETYPE_STO_DEP:
    list_item1(node1->childlist, (void **)&node2);
    semantic_parse_expr(node2, argnames, 0);
    break;

  case NODETYPE_SET_IMP:
  case NODETYPE_SET_EXP:    
  case NODETYPE_SET_CARD:
    node_error(node1, "illegal expression");
    break;
    
  default:
    node_error(node1, "unknown expression");
    break;
  }
}


static void semantic_parse_expr(node * node1, list_t argnames, int isconst)
{
  list_t iter;
  list_t iter2;
  node * node2;
  node * node3;
  node * node4;
  symbol * psym;
  char tempstr[100];

  switch(node1->type)
  {
  case NODETYPE_NULL:
    /* valtype remains as NULL */
    break;
    
  case NODETYPE_INTEGER:
    node1->valtype = symbol_table_find("NaturalNum");
    break;
    
  case NODETYPE_REAL:
    node1->valtype = symbol_table_find("Real");
    break;
    
  case NODETYPE_STRING:
    node1->valtype = symbol_table_find("Real");
    break;

  case NODETYPE_IDENTIFIER:
    if (!isconst)
    {
      /* check any arguments */
      for (iter = list_next(argnames); iter; iter = list_next(iter))
      {
        node2 = list_item(iter);
        
        if (!strcmp(node1->leaf.pchar, node2->leaf.pchar))
        {
          node1->valtype = node2->valtype;
          return;
        }
      }
    }

    psym = symbol_table_find(node1->leaf.pchar);
    
    if (!psym)
      node_error(node1, "unknown identifier '%s'", node1->leaf.pchar);
    
    node1->leaf.psym = psym;
    node1->type = NODETYPE_SYMBOL;

    if (isconst && ((SYMBOL_B_FUNCTION != psym->symtype) 
                    || (SYMBOL_FN_CAT_NONRANDOM != psym->f.category)))
      node_error(node1, "'%s' is not a non-random function", psym->name);
    
    if (SYMBOL_B_OBJECT == node1->leaf.psym->symtype)
      node1->valtype = node1->leaf.psym->o.objtype;

    else if (SYMBOL_B_FUNCTION == node1->leaf.psym->symtype)
    {
      if (list_is_empty(node1->leaf.psym->f.argtype_list))
        node1->valtype = node1->leaf.psym->f.rettype;

      else
        node_error(node1, "function '%s' expects arguments",
                   node1->leaf.psym->name);
    }

    else
      node_error(node1, "unexpected symbol '%s'", node1->leaf.psym->name);

    break;
  
  case NODETYPE_RkVECTOR:
    if (!isconst)
      node_error(node1, "unexpected RkVector here");
    
    psym = symbol_table_find("Real");
    for (iter = list_next(node1->childlist); iter; iter = list_next(iter))
    {
      node2 = (node *)list_item(iter);
      semantic_parse_expr(node2, argnames, isconst);
      if (node2->valtype != psym)
        node_error(node2, "expecting an expression of type 'Real' got '%s'",
                   node2->valtype->name);
    }
    sprintf(tempstr, "R%dVector", list_count(node1->childlist));
    node1->valtype = symbol_table_find(tempstr);
    break;

  case NODETYPE_FN_CALL:
    /* a function call has the function name and the argument list */
    list_item2(node1->childlist, (void **)&node2, (void **)&node3);

    /* first lookup the function name */
    assert(NODETYPE_IDENTIFIER == node2->type);
    
    psym = symbol_table_find(node2->leaf.pchar);
    
    if (!psym || (SYMBOL_B_FUNCTION != psym->symtype))
      node_error(node2, "'%s' is not a function name", node2->leaf.pchar);
    
    if (isconst && (SYMBOL_FN_CAT_NONRANDOM != psym->f.category))
      node_error(node1, "'%s' is not a non-random function", psym->name);
    
    node2->leaf.psym = psym;
    node2->type = NODETYPE_SYMBOL;

    node1->valtype = psym->f.rettype;
    
    /* now check the arguments */

    if (list_count(node3->childlist) != list_count(psym->f.argtype_list))
      node_error(node3, "function '%s' expects %d args got %d",
                 psym->name, list_count(psym->f.argtype_list),
                 list_count(node3->childlist));
    
    /* now parse all the arguments recursively */
    iter2 = list_next(psym->f.argtype_list);
    for (iter = list_next(node3->childlist); iter; iter = list_next(iter))
    {
      semantic_parse_expr((node *)list_item(iter), argnames, isconst);
      
      if (((node *)list_item(iter))->valtype 
          &&(((node *)list_item(iter))->valtype != (symbol *)list_item(iter2)))
        node_error((node *)list_item(iter), "expecting type '%s' got '%s'",
                   ((symbol *)list_item(iter2))->name,
                   ((node *)list_item(iter))->valtype->name);
      iter2 = list_next(iter2);
    }
    
    break;
    
  case NODETYPE_AND:
    list_item2(node1->childlist, (void **) &node2, (void **) &node3);
    semantic_parse_expr(node2, argnames, isconst);
    semantic_parse_expr(node3, argnames, isconst);
    if (strcmp(node2->valtype->name, "Boolean") 
        && strcmp(node2->valtype->name, "C_Boolean"))
      node_error(node2, "expression is not a boolean");
    
    if (strcmp(node3->valtype->name, "Boolean") 
        && strcmp(node3->valtype->name, "C_Boolean"))
      node_error(node3, "expression is not a boolean");

    node1->valtype = symbol_table_find("C_Boolean");
    break;

  case NODETYPE_OR:
    list_item2(node1->childlist, (void **) &node2, (void **) &node3);
    semantic_parse_expr(node2, argnames, isconst);
    semantic_parse_expr(node3, argnames, isconst);
    if (strcmp(node2->valtype->name, "Boolean") 
        && strcmp(node2->valtype->name, "C_Boolean"))
      node_error(node2, "expression is not a boolean");
    
    if (strcmp(node3->valtype->name, "Boolean") 
        && strcmp(node3->valtype->name, "C_Boolean"))
      node_error(node3, "expression is not a boolean");

    node1->valtype = symbol_table_find("C_Boolean");
    break;

  case NODETYPE_EQUAL:
    list_item2(node1->childlist, (void **) &node2, (void **) &node3);
    semantic_parse_expr(node2, argnames, isconst);
    semantic_parse_expr(node3, argnames, isconst);
    if (node2->valtype && node3->valtype && (node2->valtype != node3->valtype))
      node_error(node1, "can't test equality of '%s' and '%s'",
                 node2->valtype->name, node3->valtype->name);
    
    node1->valtype = symbol_table_find("C_Boolean");
    break;
    
  case NODETYPE_NOT_EQUAL:
    list_item2(node1->childlist, (void **) &node2, (void **) &node3);
    semantic_parse_expr(node2, argnames, isconst);
    semantic_parse_expr(node3, argnames, isconst);
    if (node2->valtype && node3->valtype && (node2->valtype != node3->valtype))
      node_error(node1, "can't test equality of '%s' and '%s'",
                 node2->valtype->name, node3->valtype->name);
    
    node1->valtype = symbol_table_find("C_Boolean");
    break;

  case NODETYPE_CPD_CALL:
    list_item3(node1->childlist, (void **)&node2, (void **)&node3, 
               (void **)&node4);
    /* parse the parameters */
    assert(NODETYPE_LIST == node3->type);
    for (iter = list_next(node3->childlist); iter; iter = list_next(iter))
    {
      semantic_parse_expr((node *)list_item(iter), NULL, 1);
    }
    /* parse the arguments */
    assert(NODETYPE_LIST == node3->type);
    for (iter = list_next(node4->childlist); iter; iter = list_next(iter))
      semantic_parse_expr((node *)list_item(iter), argnames, 0);

    /* record this CPD_CALL in the global list */
    global->numdist ++;
    list_insert_end(global->distlist, node1);
    node1->genname = (char *)malloc(sizeof(char)*20);
    sprintf(node1->genname, "dist%d", global->numdist);
    
    break;
    
  case NODETYPE_SYMBOL:
  case NODETYPE_IF:
  case NODETYPE_DET_DEP:
  case NODETYPE_STO_DEP:
  case NODETYPE_SET_EXP:
    node_error(node1, "illegal expression");
    break;
    
  case NODETYPE_SET_IMP:
    if (isconst)
      node_error(node1, "not a constant expression");
    
    if (1 != list_count(node1->childlist))
      node_error(node1, "set with a condition not implemented");
    break;
    
  case NODETYPE_SET_CARD:    
    if (isconst)
      node_error(node1, "not a constant expression");

    list_item1(node1->childlist, (void **)&node2);

    if (NODETYPE_SET_IMP != node2->type)
      node_error(node2, "expecting an implicit set");

    if (1 == list_count(node2->childlist))
    {
      list_item1(node2->childlist, (void **)&node3);
      node4 = NULL;

      if (node3->valtype->t.infinite)
        node_error(node3, "cardinality of '%s' is infinite",
                   node3->valtype->name);
    }
    else if (2 == list_count(node2->childlist))
    {
      list_item2(node2->childlist, (void **)&node3, (void **)&node4);
      list_insert_front(argnames, node3);
      semantic_parse_expr(node4, argnames, isconst);
      list_delete_front(argnames);
      /*node_print(node4); TODO allow this for origin functions */
      node_error(node4, "cardinality of a set with condition not implemented");
    }
    else
      assert(0);

    node1->valtype = symbol_table_find("NaturalNum");
    break;
    
  default:
    node_error(node1, "unknown expression");
    break;
  }
}

void semantic_parse_obs(node * obs)
{
  list_t iter;
  node * lhs;
  node * rhs;
  symbol * typesym;
    
  assert(NODETYPE_EQUAL == obs->type);
    
  list_item2(obs->childlist, (void **)&lhs, (void **)&rhs);

  /* handle observation of implicitly declared sets */
  if ((NODETYPE_SET_IMP == lhs->type) && (NODETYPE_SET_EXP == rhs->type) &&
      (1 == list_count(lhs->childlist)))
  {
    node * argnode;
    list_item1(lhs->childlist, (void **)&argnode);
    
    typesym = argnode->valtype;
    
    assert(SYMBOL_B_TYPE == typesym->symtype);

    /* the type can't already have its objects defined */
    if (symbol_has_fixed_objects(typesym))
      node_error(obs, "type '%s' already has %d objects defined", 
                 typesym->name, list_count(typesym->t.objlist));

    typesym->t.has_obsobjs = 1;

    for (iter = list_next(rhs->childlist); iter; iter = list_next(iter))
    {
      node * objnode;
      objnode = list_item(iter);
      assert(NODETYPE_IDENTIFIER == objnode->type);
      if (symbol_table_new_object(typesym, objnode->leaf.pchar))
        node_error(objnode, "symbol '%s' redefined", objnode->leaf.pchar);
        
      objnode->type = NODETYPE_SYMBOL;
      objnode->leaf.psym = symbol_table_find(objnode->leaf.pchar);
    }
  }
  /* zero-ary function observation */
  else if (NODETYPE_IDENTIFIER == lhs->type)
  {
    symbol * fnsym;
    list_t argnames;
      
    argnames = list_alloc();
    
    semantic_parse_expr(lhs, argnames, 0);
    semantic_parse_expr(rhs, argnames, 0);
    
    list_free(argnames);
    
    assert(NODETYPE_SYMBOL == lhs->type);
    
    fnsym = lhs->leaf.psym;
    
    if (SYMBOL_B_FUNCTION != fnsym->symtype)
      node_error(lhs, "illegal observation of '%s'", fnsym->name);
    
    fnsym->f.zary_obs = rhs;

    fnsym->f.has_obs = 1;
  }
  else if (NODETYPE_FN_CALL == lhs->type)
  {
    node * fnnode;
    symbol * fnsym;
    node * parnode;
    list_t argnames;
      
    argnames = list_alloc();
    
    semantic_parse_expr(lhs, argnames, 0);
    semantic_parse_expr(rhs, argnames, 0);
    
    list_free(argnames);
      
    list_item2(lhs->childlist, (void *)&fnnode, (void *)&parnode);

    assert(NODETYPE_SYMBOL == fnnode->type);
    
    fnsym = fnnode->leaf.psym;

    assert(SYMBOL_B_FUNCTION == fnsym->symtype);

    fnsym->f.has_obs = 1;
    
    if (list_is_empty(fnsym->f.argtype_list))
    {
      fnsym->f.zary_obs = rhs;
    }
    else
    {
      node * objnode;
      symbol * objsym;
      list_item1(parnode->childlist, (void **)&objnode);
      
      if ((NODETYPE_SYMBOL != objnode->type) 
          || (SYMBOL_B_OBJECT != objnode->leaf.psym->symtype))
      {
        node_error(objnode, "illegal observation, expecting an object arg");
      }

      objsym = objnode->leaf.psym;
      list_insert_end(objsym->o.obs_attr_vals, list_alloc2(fnsym, rhs));
    }
  }
  else
  {
    node_error(obs, "illegal observation");
  }
}

void semantic_parse_qry(node * qry)
{
  list_t argnames;
  
  argnames = list_alloc();
  semantic_parse_expr(qry, argnames, 0);
  list_free(argnames);  
}

void semantic_parse_num(symbol * type, list_t id_eq_id, node * dep)
{
  list_t argnames;
  list_t iter;
  symbol * origsym;

  if (type->t.has_guarobjs)
    node_error(dep, "can't given number statement for type '%s' with "
               "guaranteed objects", type->name);
  
  argnames = list_alloc();
  origsym = NULL;

  for (iter = list_next(id_eq_id); iter; iter=list_next(iter))
  {
    node * equal;
    node * orignode;
    node * argnode;
    
    equal = (node *)list_item(iter);
    
    assert(NODETYPE_EQUAL == equal->type);

    list_item2(equal->childlist, (void **)&orignode, (void **)&argnode);

    assert(NODETYPE_IDENTIFIER == orignode->type);
    assert(NODETYPE_IDENTIFIER == argnode->type);
    
    origsym = symbol_table_find(orignode->leaf.pchar);

    if (!origsym || (SYMBOL_B_FUNCTION != origsym->symtype)
        || (SYMBOL_FN_CAT_ORIGIN != origsym->f.category)
        || ((symbol *)list_item(list_next(origsym->f.argtype_list)) != type))
      node_error(orignode, "'%s' is not an origin function for type '%s'",
                 orignode->leaf.pchar, type->name);
    
    orignode->leaf.psym = origsym;
    orignode->type = NODETYPE_SYMBOL;
    
    argnode->valtype = origsym->f.rettype;
    
    list_insert_end(argnames, argnode);
  }

  /* we'll convert the number statement into a function declration */
  if (list_is_empty(argnames))
  {
    char * fnname;
    symbol * numfn;
    fnname = malloc(100);
    sprintf(fnname, "num_null_%s", type->name);
    semantic_parse_fndecl(SYMBOL_FN_CAT_NUMBER,
                          symbol_table_find("NaturalNum"), fnname,
                          list_alloc());
    semantic_parse_fndef(fnname, list_alloc(), dep);

    numfn = symbol_table_find(fnname);
    type->t.null_numfn = numfn;
  }
  else
  {
    char * fnname;
    node * argnode;
    symbol * numfn;
    
    list_item1(argnames, (void **)&argnode);

    fnname = malloc(100);
    sprintf(fnname, "num_%s_%s", argnode->valtype->name, type->name);
    semantic_parse_fndecl(SYMBOL_FN_CAT_NUMBER,
                          symbol_table_find("NaturalNum"), fnname,
                          list_alloc1(argnode->valtype));
    semantic_parse_fndef(fnname, argnames, dep);

    numfn = symbol_table_find(fnname);
    origsym->f.numfn = numfn;
  }
  
  /*semantic_parse_dep(dep, argnames);*/
}

void semantic_parse_fndef(char * fnname, list_t argnames, node * dep)
{
  symbol * fnsym;
  list_t iter;
  list_t iter2;

  fnsym = symbol_table_find(fnname);
  
  if (!fnsym)
    node_error(dep, "function '%s' defined without a declaration", fnname);

  if (SYMBOL_B_FUNCTION != fnsym->symtype)
    node_error(dep, "symbol '%s' redefined", fnname);
  
  if (NULL != fnsym->f.tree)
    node_error(dep, "function '%s' redefined", fnname);

  fnsym->f.argname_list = argnames;
  fnsym->f.tree = dep;

  if (list_count(fnsym->f.argname_list) != list_count(fnsym->f.argtype_list))
    node_error(dep, "wrong number of arguments in definition of '%s'");
  
  if ((SYMBOL_FN_CAT_RANDOM != fnsym->f.category)
      && (SYMBOL_FN_CAT_NUMBER != fnsym->f.category))
    node_error(dep, "only random/number functions can have a dependency");

  /* set the type of all the argument names */
  iter2 = list_next(fnsym->f.argtype_list);
  for (iter = list_next(fnsym->f.argname_list); iter; iter = list_next(iter))
  {
    ((node *)list_item(iter))->valtype = (symbol *)list_item(iter2);
    iter2 = list_next(iter2);
  }
  
  semantic_parse_dep(fnsym->f.tree, fnsym->f.argname_list);
  sem_analysis_fn(fnsym, fnsym->f.tree);
  sem_simple_fn(fnsym);
}

void semantic_parse_fndecl(int category, symbol * ret_type, char * fnname, 
                           list_t argtypes)
{
  symbol * fnsym;
  symbol * typesym;    

  /* if the symbol already exists then its an error */
  if (symbol_table_new_function_decl(category, ret_type, fnname, argtypes))
    sem_fatal("symbol '%s' redefined", fnname);

  fnsym = symbol_table_find(fnname);
  
  switch(fnsym->f.category)
  {
  case SYMBOL_FN_CAT_RANDOM:
  case SYMBOL_FN_CAT_NUMBER:
    if (!list_is_empty(fnsym->f.argtype_list))
    {
      if (list_count(fnsym->f.argtype_list) > 1)
        sem_fatal("function '%s' can have at most one argument",
                  fnsym->name);

      /* if there is one argument then its an attribute of the argument
       * type */
      /* extract the symbol for the argument type */
      list_item1(fnsym->f.argtype_list, (void **)&typesym);
      assert(SYMBOL_B_TYPE == typesym->symtype);

      if (typesym->builtin)
        sem_fatal("function '%s' on builtin type '%s' not "
                  "allowed", fnsym->name, typesym->name);

      /* add this function to the list of attributes of the type */
      list_insert_end(typesym->t.attrs, fnsym);
    }
    break;

  case SYMBOL_FN_CAT_ORIGIN:
    /* origin functions have exactly one argument */
    if (1 != list_count(fnsym->f.argtype_list))
      sem_fatal("origin function '%s' should have exactly one arg",
                fnsym->name);
    list_item1(fnsym->f.argtype_list, (void **)&typesym);
    assert(SYMBOL_B_TYPE == typesym->symtype);
        
    if (typesym->builtin)
      sem_fatal("origin function '%s' on builtin type '%s' not "
                "allowed", fnsym->name, typesym->name);

    /* add this function to the list of origin functions of the type */
    list_insert_end(typesym->t.origfns, fnsym);

    /* currently, we only support one origin function */
    if (list_count(typesym->t.origfns) > 1)
    {
      sem_fatal("type '%s' has more than one origin function",
                typesym->name);
    }

    /* add this function to the list of attributes of the type */
    list_insert_end(typesym->t.attrs, fnsym);

    break;

  case SYMBOL_FN_CAT_NONRANDOM:
    if (!fnsym->f.rettype->builtin)
      sem_fatal("non-random function '%s' must have a builtin return type",
                fnsym->name);
    break;
    
  default:
    assert(0);                               /* unknown function category! */
    break;
  }

  if (!fnsym->f.rettype->builtin)
    list_insert_end(fnsym->f.rettype->t.incoming_fns, fnsym);
}

/* we will update all the parents of this function */
static void sem_analysis_fn(symbol * fnsym, node * dep)
{
  if (NODETYPE_SYMBOL == dep->type)
  {
    symbol * sym;
    
    sym = dep->leaf.psym;

    if (SYMBOL_B_FUNCTION == sym->symtype)
      list_add(sym->f.childfns, fnsym);
  }
  
  else if (NODETYPE_FN_CALL == dep->type)
  
  {
    node * fnarg;

    node * callfn;
    symbol * callfnsym;
    
    node * callargs;
    node * callarg;
  
    if (list_is_empty(fnsym->f.argname_list))
    {
      node_error(dep, "illegal expression");
    }
    
    list_item1(fnsym->f.argname_list, (void **)&fnarg);

    list_item2(dep->childlist, (void **)&callfn, (void **)&callargs);
    callfnsym = callfn->leaf.psym;
    list_item1(callargs->childlist, (void **)&callarg);

    if ((NODETYPE_IDENTIFIER == callarg->type) && 
        !strcmp(callarg->leaf.pchar, fnarg->leaf.pchar))
      list_add(callfnsym->f.childattrs, fnsym);

    else if (NODETYPE_FN_CALL == callarg->type)
    {
      node * nested_callfn;
      symbol * nested_callfnsym;
      
      node * nested_callargs;
      node * nested_callarg;

      list_item2(callarg->childlist, (void **)&nested_callfn,
                 (void **)&nested_callargs);
      nested_callfnsym = nested_callfn->leaf.psym;
      list_item1(nested_callargs->childlist, (void **)&nested_callarg);

      if ((NODETYPE_IDENTIFIER == nested_callarg->type) && 
          !strcmp(nested_callarg->leaf.pchar, fnarg->leaf.pchar))
      {
        list_t iter;
        list_t iter2;
        
        for (iter = list_next(callfnsym->f.child_in_attrs); iter;
             iter = list_next(iter))
        {
          iter2 = list_item(iter);
          if (list_item(list_next(iter2)) == nested_callfnsym)
            break;
        }
        if (!iter)
        {
          iter2 = list_alloc1(nested_callfnsym);
          list_insert_end(callfnsym->f.child_in_attrs, iter2);
        }
        
        list_add(iter2, fnsym);
        
        list_add(nested_callfnsym->f.childattrs, fnsym);
      }
      else
        node_error(nested_callarg, "illegal expression");
    }
    
    else
      node_error(dep, "illegal expression");
  }

  else if (NODETYPE_SET_IMP == dep->type)
  {
    node * argnode;
    
    argnode = (node *)list_item(list_next(dep->childlist));
    
    list_add(argnode->valtype->t.childfns, fnsym);
  }  
  
  else if (dep->childlist)
  {
    list_t iter;
    
    for (iter = list_next(dep->childlist); iter; iter = list_next(iter))
      sem_analysis_fn(fnsym, (node *)list_item(iter));
  }
}

/* analyze if its a one-liner of TabularCPD or UniformCPD or
 * MultinomialCPD */
static void sem_simple_fn(symbol * fnsym)
{
  if (NODETYPE_STO_DEP == fnsym->f.tree->type)
  {
    node * cpd;
    node * cpdname;
    node * cpdparams;
    node * cpdargs;
    
    list_item1(fnsym->f.tree->childlist, (void **)&cpd);
    
    assert(NODETYPE_CPD_CALL == cpd->type);
    
    list_item3(cpd->childlist, (void **)&cpdname, (void **)&cpdparams,
               (void **)&cpdargs);
    
    if (!strcmp(cpdname->leaf.pchar, "UniformChoice") &&
        (list_count(cpdargs->childlist)==1))
    {
      node * cpdarg;
      
      list_item1(cpdargs->childlist, (void **)&cpdarg);
      
      if ((NODETYPE_SET_IMP == cpdarg->type) 
          && (1==list_count(cpdarg->childlist)))
      {
        fnsym->f.cpdtype.uncond_uniform = 1;
      }
    }
  }
}

void semantic_parse_guarobj(symbol * typesym, node * objnode)
{
  if (typesym->t.null_numfn)
    node_error(objnode, 
               "type '%s' can't have number statement and guaranteed objects",
               typesym->name);

  if (!list_is_empty(typesym->t.origfns))
    node_error(objnode,
               "type '%s' can't have origin functions and guaranteed objects",
               typesym->name);

  if (symbol_table_new_object(typesym, objnode->leaf.pchar))
  {
    node_error(objnode, "symbol '%s' redefined", objnode->leaf.pchar);
  }

  typesym->t.has_guarobjs = 1;
}

static void sem_block_child(symbol * fnsym, symbol * childfnsym)
{
  list_t iter;
  list_t iter2;
  
  if (global->no_block)
    return;
  
  /* blocking children must be plain random functions */
  if (SYMBOL_FN_CAT_RANDOM != childfnsym->f.category)
    return;
  
  /* we can't sample the child function if that function could have been
   * observed */
  if (childfnsym->f.has_obs)
    return;

  /* ensure that the childfnsym has same child functions as the fnsym */
  for (iter = list_next(childfnsym->f.childfns); iter; iter = list_next(iter))
  {
    symbol * childchildfn;
    
    childchildfn = (symbol *) list_item(iter);

    if (!list_find(fnsym->f.childfns, childchildfn))
      return;
  }

  /* ensure that the childfnsym has same child attributes as the fnsym */
  for (iter = list_next(childfnsym->f.childattrs); iter; 
       iter = list_next(iter))
  {
    symbol * childchildattr;
    
    childchildattr = (symbol *) list_item(iter);

    if (!list_find(fnsym->f.childattrs, childchildattr))
      return;
  }

  /* now repeat the check for the child attributes of incoming functions */
  for (iter = list_next(childfnsym->f.child_in_attrs); iter; 
       iter = list_next(iter))
  {
    list_t child_inlist;
    symbol * childinfn;

    list_t inlist;
    symbol * infn;    
    
    child_inlist = list_item(iter);
    child_inlist = list_next(child_inlist);  /* make it point to incoming fn */
    childinfn = (symbol *) list_item(child_inlist);
    
    for (iter2 = list_next(fnsym->f.child_in_attrs); iter2;
         iter2 = list_next(iter2))
    {
      inlist = list_item(iter2);
      inlist = list_next(inlist);   /* make it point to incoming fn */
      infn = (symbol *) list_item(inlist);

      if (infn == childinfn)
        break;
    }

    if (!iter2)
      return;

    for (iter2 = list_next(child_inlist); iter2; iter2 = list_next(iter2))
      if (!list_find(inlist, list_item(iter2)))
        return;
  }

  list_insert_end(fnsym->f.block_child, childfnsym);
}
